﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using anks.Lib.Config;

namespace anks.Components
{
    public static class ComponentParamsExt
    {
        public static void AddConfig(this Component.ComponentParams componentParams, ConfigGroup configGroup)
        {
            if (configGroup != null)
                foreach (var item in configGroup)
                    componentParams.Add(item.Key, new Component.ComponentParam { asString = item.Value.asString });
        }
    }

    public class ComponentRegistry
    {
        protected class FactoryRegistration
        {
            public string TypeName { get; set; }
            public string Name { get; set; }
            public bool IsDefault { get; set; }
            public CustomFactory Factory { get; set; }
        }

        private List<FactoryRegistration> _factoryRegistry;
        protected List<FactoryRegistration> FactoryRegistry { get { return _factoryRegistry; } }
        protected Lookup<string, FactoryRegistration> Factories { 
            get 
            {
                return (Lookup<string, FactoryRegistration>)FactoryRegistry.ToLookup(p => p.TypeName, p => p);
            } 
        }

        protected Dictionary<string, CustomFactory> FactoriesByType(string typeName)
        {
            return (Dictionary<string, CustomFactory>)Factories[typeName].ToDictionary(p => p.Name, p => p.Factory);
        }

        protected CustomFactory GetDefaultFactory(string typeName)
        {
            FactoryRegistration reg = Factories[typeName].Single(p => p.IsDefault);
            return (reg != null) ? reg.Factory : null;
        }

        protected bool FindFactory(string typeName, string name, out CustomFactory factory)
        {
            if (FactoriesByType(typeName).ContainsKey(name))
                factory = FactoriesByType(typeName)[name];
            else
                factory = null;
            return factory != null;
        }

        protected CustomFactory GetFactory(string typeName, string name)
        {
            CustomFactory factory;
            if (FindFactory(typeName, name, out factory))
                return factory;
            return null;
        }

        public void RegisterFactory(string typeName, string name, CustomFactory factory)
        {
            RegisterFactory(typeName, name, factory, false);
        }

        public void RegisterFactory(string typeName, string name, CustomFactory factory, bool isDefault)
        {
            FactoryRegistry.Add(new FactoryRegistration { TypeName = typeName, Name = name, Factory = factory, IsDefault = isDefault});
        }

        public void UnregisterFactory(string typeName, string name)
        {
            FactoryRegistry.RemoveAll(p => p.TypeName == typeName && p.Name == name);
        }

        public ComponentRegistry()
        {
            _factoryRegistry = new List<FactoryRegistration>();
            _implementations = new Dictionary<string, Component>();
        }

        public ComponentRegistry(Config config)
            : this()
        {
            this.config = config;
        }

        public Config config { get; set; }

        private Dictionary<string, Component> _implementations;
        public Dictionary<string, Component> Implementations { get { return _implementations; } }

        private string GetComponentImplementationName(string typeName)
        {
            return (config.ContainsKey(typeName) && config[typeName].ContainsKey("mode")) ? config[typeName]["mode"].asString : "";
        }

        private Component.ComponentParams BuildComponentParams(string typeName, string name)
        {
            Component.ComponentParams cp = new Component.ComponentParams();
            ConfigGroup group;
            if (config.TryGetValue(typeName, out group))
                cp.AddConfig(group);
            if (config.TryGetValue(typeName, out group))
                cp.AddConfig(group);
            return cp;
        }

        public Component Implementation(string typeName)
        {
            if (!Implementations.ContainsKey(typeName))
            {
                string name = GetComponentImplementationName(typeName);
                CustomFactory fact;
                if (!FindFactory(typeName, name, out fact))
                    fact = GetDefaultFactory(typeName);
                Component.ComponentParams cp = BuildComponentParams(typeName, name);
                Implementations.Add(typeName, fact.Create(cp));
            }
            return Implementations[typeName];
        }

    }
}
